package org.kca.yarmour.utils;

import static org.lwjgl.opengl.GL11.*;

import org.kca.graphics.FontPalette;
import org.kca.graphics.Texture;

public class KcaGLUtils
{
    public static final int BUTTON_ENABLED = 0;
    public static final int BUTTON_DISABLED = 1;
    public static final int BUTTON_DEPRESSED = 2;
    
    /**
     * Fills a rectangle with the foreground color
     * @param x the x coordinate of the top left corner
     * @param y the y coordinate of the top left corner
     * @param w the width
     * @param h the height
     */
    public static void fillRect(int x, int y, int w, int h)
    {
        glBindTexture(GL_TEXTURE_2D, 0);
        glBegin(GL_QUADS);
        {
            glVertex2f(x, y);
            glVertex2f(x + w, y);
            glVertex2f(x + w, y + h);
            glVertex2f(x, y + h);
        }
        glEnd();
    }
    
    /**
     * Draws a hollow rectangle with the foreground color
     * @param x the x coordinate of the top left corner
     * @param y the y coordinate of the top left corner
     * @param w the width
     * @param h the height
     */
    public static void drawRect(int x, int y, int w, int h)
    {
        glBindTexture(GL_TEXTURE_2D, 0);
        glBegin(GL_LINE_LOOP);
        {
            glVertex2f(x + 0.25f, y + 0.25f);
            glVertex2f(x + w - 0.25f, y + 0.25f);
            glVertex2f(x + w - 0.25f, y + h - 0.25f);
            glVertex2f(x + 0.25f, y + h - 0.25f);
        }
        glEnd();
    }
    
    /**
     * Draws a circle centered at a particular point.<br>
     * [Code referenced from: http://slabode.exofire.net/circle_draw.shtml ]
     * @param cx the x-coordinate of the center of the circle
     * @param cy the y-coordinate of the center of the circle
     * @param r the radius of the circle
     * @param segs the number of segments. Higher numbers result in smoother circles at the
     * cost of performance
     */
    public static void drawCircle(float cx, float cy, float r, int segs)
    {
        glBindTexture(GL_TEXTURE_2D, 0);
        double theta = 2 * Math.PI / segs; 
        double tangent_factor = Math.tan(theta);
        double radial_factor = Math.cos(theta);
        float x = r;//we start at angle = 0 
        float y = 0; 
        
        glBegin(GL_LINE_LOOP);
        {
            for(int ii = 0; ii < segs; ii++) 
            { 
                glVertex2f(x + cx, y + cy); 
                float tx = -y; 
                float ty = x; 

                x += tx * tangent_factor; 
                y += ty * tangent_factor; 
                x *= radial_factor; 
                y *= radial_factor; 
            } 
        }
        glEnd(); 
    }
    
    /**
     * Fills a circle centered at a particular point.<br>
     * [Code referenced from: http://slabode.exofire.net/circle_draw.shtml ]
     * @param cx the x-coordinate of the center of the circle
     * @param cy the y-coordinate of the center of the circle
     * @param r the radius of the circle
     * @param segs the number of segments. Higher numbers result in smoother circles at the
     * cost of performance
     */
    public static void fillCircle(float cx, float cy, float r, int segs)
    {
        glBindTexture(GL_TEXTURE_2D, 0);
        double theta = 2 * Math.PI / segs; 
        double tangetial_factor = Math.tan(theta);
        double radial_factor = Math.cos(theta);
        float x = r;//we start at angle = 0 
        float y = 0; 
        
        glBegin(GL_TRIANGLE_FAN);
        {
            glVertex2f(cx, cy);
            for(int ii = 0; ii <= segs; ii++) 
            { 
                glVertex2f(x + cx, y + cy); 
                float tx = -y; 
                float ty = x; 
    
                x += tx * tangetial_factor; 
                y += ty * tangetial_factor; 
                x *= radial_factor; 
                y *= radial_factor; 
            } 
        }
        glEnd(); 
    }
    
    /**
     * Draws a tooltip at a specified position
     * @param x the x coordinate of the top left of the tooltip box
     * @param y the y coordinate of the top left of the tooltip box
     * @param font the font to use
     * @param text the text of the tooltip
     */
    public static void drawTooltip(int x, int y, FontPalette font, String text)
    {
        int w = font.stringWidth(text) + 3;
        int h = font.getHeight() + 2;
        glColor4f(1, 1, 0.8f, 1);
        fillRect(x, y, w, h);
        glColor4f(0, 0, 0, 1);
        glLineWidth(1);
        drawRect(x, y, w, h);
        glColor4f(0, 0, 0, 1);        
        font.drawString(text, x + 2, y + 1);
    }
    
    /**
     * Draws a button at the specified position, with the specified text
     * @param x the x-coordinate of the top left point
     * @param y the y-coordinate of the top left point
     * @param w the width of the button
     * @param h the height of the button
     * @param font the font of the button text
     * @param text the button text
     * @param mode either BUTTON_ENABLED, BUTTON_DEPRESSED, or BUTTON_DISABLED
     */
    public static void drawButton(int x, int y, int w, int h, FontPalette font, String text, int mode)
    {
        switch (mode) // color of background
        {
        case BUTTON_DISABLED:
            glColor4f(0.9f, 0.9f, 0.9f, 1);
            break;    
        case BUTTON_ENABLED:
            glColor4f(0.75f, 0.75f, 0.75f, 1);
            break;
        case BUTTON_DEPRESSED:
            glColor4f(0.6f, 0.6f, 0.6f, 1);
            break;
        }
        fillRect(x, y, w, h);
        switch (mode) // bottom-right shadow
        {
        case BUTTON_DISABLED:
            glColor4f(0.4f, 0.4f, 0.4f, 1);
            break;    
        case BUTTON_ENABLED:
            glColor4f(0.4f, 0.4f, 0.4f, 1);
            break;
        case BUTTON_DEPRESSED:
            glColor4f(0.9f, 0.9f, 0.9f, 1);
            break;
        }
        glLineWidth(1);
        drawLine(x + w, y, x + w, y + h);
        drawLine(x, y + h, x + w, y + h);
        switch (mode) // top-left shadow
        {
        case BUTTON_DISABLED:
            glColor4f(0.9f, 0.9f, 0.9f, 1);
            break;    
        case BUTTON_ENABLED:
            glColor4f(0.9f, 0.9f, 0.9f, 1);
            break;
        case BUTTON_DEPRESSED:
            glColor4f(0.4f, 0.4f, 0.4f, 1);
            break;
        }
        
        drawLine(x, y, x + w, y);
        drawLine(x, y, x, y + h);
        int lx = ImageUtils.centerAlignX(font, x, x + w, text);
        switch (mode) // text color
        {
        case BUTTON_DISABLED:
            glColor4f(0.5f, 0.5f, 0.5f, 1);
            break;
        case BUTTON_DEPRESSED:
        case BUTTON_ENABLED:
            glColor4f(0, 0, 0, 1);
            break;
        }
        
        font.drawString(text, lx, y + (h - font.getHeight()) / 2);
    }
    
    /**
     * Draws a button at the specified position, with the specified text
     * @param dims an array with 4 elements describing the x, y, width and height of the button
     * @param font the font of the button text
     * @param text the button text
     * @param mode either BUTTON_ENABLED, BUTTON_DEPRESSED, or BUTTON_DISABLED
     */
    public static void drawButton(int[] dims, FontPalette font, String text, int mode)
    {
        drawButton(dims[0], dims[1], dims[2], dims[3], font, text, mode);
    }
    
    /**
     * Draws a button at the specified position, with the specified image
     * @param x the x-coordinate of the top left point
     * @param y the y-coordinate of the top left point
     * @param w the width of the button
     * @param h the height of the button
     * @param image the image to draw
     * @param mode either BUTTON_ENABLED, BUTTON_DEPRESSED, or BUTTON_DISABLED
     */
    public static void drawButton(int x, int y, int w, int h, Texture image, int mode)
    {
        switch (mode) // color of background
        {
        case BUTTON_DISABLED:
            glColor4f(0.9f, 0.9f, 0.9f, 1);
            break;    
        case BUTTON_ENABLED:
            glColor4f(0.75f, 0.75f, 0.75f, 1);
            break;
        case BUTTON_DEPRESSED:
            glColor4f(0.6f, 0.6f, 0.6f, 1);
            break;
        }
        fillRect(x, y, w, h);
        switch (mode) // bottom-right shadow
        {
        case BUTTON_DISABLED:
            glColor4f(0.4f, 0.4f, 0.4f, 1);
            break;    
        case BUTTON_ENABLED:
            glColor4f(0.4f, 0.4f, 0.4f, 1);
            break;
        case BUTTON_DEPRESSED:
            glColor4f(0.9f, 0.9f, 0.9f, 1);
            break;
        }
        glLineWidth(1);
        drawLine(x + w, y, x + w, y + h);
        drawLine(x, y + h, x + w, y + h);
        switch (mode) // top-left shadow
        {
        case BUTTON_DISABLED:
            glColor4f(0.9f, 0.9f, 0.9f, 1);
            break;    
        case BUTTON_ENABLED:
            glColor4f(0.9f, 0.9f, 0.9f, 1);
            break;
        case BUTTON_DEPRESSED:
            glColor4f(0.4f, 0.4f, 0.4f, 1);
            break;
        }
        
        drawLine(x, y, x + w, y);
        drawLine(x, y, x, y + h);
        int lx = x + (w - image.getImageWidth()) / 2;
        int ly = y + (h - image.getImageHeight()) / 2;
        switch (mode) // text color
        {
        case BUTTON_DISABLED:
            drawImage(image, lx, ly, new float[] {1, 1, 1, 0.4f});
            break;
        case BUTTON_DEPRESSED:
        case BUTTON_ENABLED:
            drawImage(image, lx, ly);
            break;
        }
        
    }
    
    /**
     * Draws a line between two points
     * @param x1 x coordinate of point 1
     * @param y1 y coordinate of point 1
     * @param x2 x coordinate of point 2
     * @param y2 y coordinate of point 2
     */
    public static void drawLine(int x1, int y1, int x2, int y2)
    {
        glBindTexture(GL_TEXTURE_2D, 0);
        glBegin(GL_LINES);
        {
            glVertex2f(x1, y1);
            glVertex2f(x2, y2);
        }
        glEnd();
    }
    
    /**
     * Draws a 2D image the size of the texture provided, with the specified color profile
     * @param tex the texture containing the image
     * @param x the x coordinate of the top left corner
     * @param y the y coordinate of the top left corner
     * @param colors an array of size 4 with the colors to apply to the texture
     */
    public static void drawImage(Texture tex, int x, int y, float[] colors)
    {
        glColor4f(colors[0], colors[1], colors[2], colors[3]);
        glPushMatrix();
        glTranslatef(x, y, 0);
        tex.bind();
        glBegin(GL_QUADS);
        {
            glTexCoord2f(0, 0);
            glVertex2f(0, 0);

            glTexCoord2f(0, tex.getHeight());
            glVertex2f(0, tex.getImageHeight());

            glTexCoord2f(tex.getWidth(), tex.getHeight());
            glVertex2f(tex.getImageWidth(), tex.getImageHeight());

            glTexCoord2f(tex.getWidth(), 0);
            glVertex2f(tex.getImageWidth(), 0);
        }
        glEnd();
        glPopMatrix();
    }
    
    /**
     * Draws a 2D image the size of the texture provided
     * @param tex the texture containing the image
     * @param x the x coordinate of the top left corner
     * @param y the y coordinate of the top left corner
     */
    public static void drawImage(Texture tex, int x, int y)
    {
        glColor4f(1, 1, 1, 1);
        glPushMatrix();
        glTranslatef(x, y, 0);
        tex.bind();
        glBegin(GL_QUADS);
        {
            glTexCoord2f(0, 0);
            glVertex2f(0, 0);

            glTexCoord2f(0, tex.getHeight());
            glVertex2f(0, tex.getImageHeight());

            glTexCoord2f(tex.getWidth(), tex.getHeight());
            glVertex2f(tex.getImageWidth(), tex.getImageHeight());

            glTexCoord2f(tex.getWidth(), 0);
            glVertex2f(tex.getImageWidth(), 0);
        }
        glEnd();
        glPopMatrix();
    }
    
    /**
     * Draws a 2D image with the texture provided of a specified size
     * @param tex the texture containing the image
     * @param x the x coordinate of the top left corner
     * @param y the y coordinate of the top left corner
     * @param w the width of the image
     * @param h the height of the image
     */
    public static void drawScaledImage(Texture tex, int x, int y, int w, int h)
    {
        glColor4f(1, 1, 1, 1);
        glPushMatrix();
        glTranslatef(x, y, 0);
        tex.bind();
        glBegin(GL_QUADS);
        {
            glTexCoord2f(0, 0);
            glVertex2f(0, 0);

            glTexCoord2f(0, tex.getHeight());
            glVertex2f(0, h);

            glTexCoord2f(tex.getWidth(), tex.getHeight());
            glVertex2f(w, h);

            glTexCoord2f(tex.getWidth(), 0);
            glVertex2f(w, 0);
        }
        glEnd();
        glPopMatrix();
    }
    
    /**
     * Draws a clipped portion of a 2D image with the texture provided.
     * @param tex the texture containing the image
     * @param x the x coordinate of the original top left of the image
     * @param y the y coordinate of the original top left of the image
     * @param bx the x coordinate of the top left corner of the internal clipping bounding box
     * @param by the y coordinate of the top left corner of the internal clipping bounding box
     * @param bw the width of the internal clipping bounding box
     * @param bh the height of the internal clipping bounding box
     */
    public static void drawClippedImage(Texture tex, int x, int y, int bx, int by, int bw, int bh)
    {
        glColor4f(1, 1, 1, 1);
        glPushMatrix();
        glTranslatef(x, y, 0);
        int clipx = Math.min(Math.max(0, bx - x), x + tex.getImageWidth());
        int clipy = Math.min(Math.max(0, by - y), y + tex.getImageHeight());
        int endx = Math.min(Math.max(0, bx + bw - x), x + tex.getImageWidth());
        int endy = Math.min(Math.max(0, by + bh - y), x + tex.getImageHeight());
        float dw = tex.getImageWidth() / tex.getWidth();
        float dh = tex.getImageHeight() / tex.getHeight();
        tex.bind();
        glBegin(GL_QUADS);
        {
            glTexCoord2f(clipx / dw, clipy / dh);
            glVertex2f(clipx, clipy);

            glTexCoord2f(clipx / dw, endy / dh);
            glVertex2f(clipx, endy);

            glTexCoord2f(endx / dw, endy / dh);
            glVertex2f(endx, endy);

            glTexCoord2f(endx / dw, clipy / dh);
            glVertex2f(endx, clipy);
        }
        glEnd();
        glPopMatrix();
    }
    
    /**
     * Draws a row of tabs with the specified text.
     * @param x the x coordinate of the top left corner of the row
     * @param y the y coordinate of the top left corner of the row
     * @param w the width of the tab row
     * @param h the height of the tab row
     * @param tabw the width of the middle part of the tab. The full width of each tab
     * is given by (tabw + 2 * tabg)
     * @param tabg the width of the sloping part of the tab
     * @param text the text to be shown on the tab row. The length of this array also
     * specifies the number of tabs
     * @param font the font 
     * @param activeTab the index of the active tab
     */
    public static void drawTabs(int x, int y, int w, int h, int tabw, int tabg, String[] text, 
            FontPalette font, int activeTab)
    {
        int numTabs = text.length;
        glColor4f(0.4f, 0.4f, 0.4f, 1);
        fillRect(x, y, w, h);
        for (int i = numTabs - 1; i >= 0; i--)
        {
            glBindTexture(GL_TEXTURE_2D, 0);
            glColor4f(0.7f, 0.7f, 0.7f, 1);
            int xl = x + (tabw + tabg) * i;
            glBegin(GL_QUADS);
            {  
                glVertex2f(xl, y + h);
                glVertex2f(xl + tabg, y);
                glVertex2f(xl + tabg + tabw, y);
                glVertex2f(xl + tabg + tabg + tabw, y + h);
            }
            glEnd();
            glColor4f(0, 0, 0, 1);
            glBegin(GL_LINE_STRIP);
            {
                glVertex2f(xl, y + h);
                glVertex2f(xl + tabg, y);
                glVertex2f(xl + tabg + tabw, y);
                glVertex2f(xl + tabg + tabg + tabw, y + h);
                glVertex2f(xl, y + h);
            }
            glEnd();
            font.drawString(text[i], ImageUtils.centerAlignX(font, xl + tabg + tabw / 2, text[i]), 
                    y + (h - font.getHeight()) / 2);
        }
        // draw active tab
        if (activeTab >= 0 && activeTab < numTabs)
        {
            glBindTexture(GL_TEXTURE_2D, 0);
            glColor4f(1, 1, 1, 1);
            int xla = x + (tabw + tabg) * activeTab;
            glBegin(GL_QUADS);
            {
                glVertex2f(xla, y + h);
                glVertex2f(xla + tabg, y);
                glVertex2f(xla + tabg + tabw, y);
                glVertex2f(xla + tabg + tabg + tabw, y + h);
            }
            glEnd();
            glColor4f(0, 0, 0, 1);
            glBegin(GL_LINE_LOOP);
            {
                glVertex2f(xla, y + h);
                glVertex2f(xla + tabg, y);
                glVertex2f(xla + tabg + tabw, y);
                glVertex2f(xla + tabg + tabg + tabw, y + h);
            }
            glEnd();
            font.drawString(text[activeTab], ImageUtils.centerAlignX(font, xla + tabg + tabw / 2, 
                    text[activeTab]), y + (h - font.getHeight()) / 2);
        }
        drawLine(x, y + h, x + w, y + h);
    }
    
    /**
     * Sets the OpenGL foreground color using Java AWT colors
     * @param col the color to set
     */
    public static void setColor(java.awt.Color col)
    {
        float r = col.getRed() / 255.0f;
        float g = col.getGreen() / 255.0f;
        float b = col.getBlue() / 255.0f;
        float a = col.getAlpha() / 255.0f;
        glColor4f(r, g, b, a);
    }
    
    
}
